/* 
 * Author: vdaras
 */

#ifndef COMPONENT_H
#define	COMPONENT_H

#include "../NonCopyable.h"
#include "../sdl/Surface.h"
#include "FocusServer.h"
#include <string>
#include <list>

class KeyboardEvent;
class MouseEvent;

namespace gui
{

class ActionListener;
class Container;

/**
 * Abstract class providing an interface for the gui's user controls, containers
 * etc. This class is the fundamental block of the whole system since it uses
 * the composite pattern and the Component is the leaf of the composite tree.
 * All coordinates and rectangles stored are RELATIVE to the parent of the Component, though
 * regular accessor methods (like GetX) return absolute values. 
 */

class Component : public NonCopyable
{
    friend Component* FocusServer::ResolveMouseFocus(const MouseEvent& event, gui::Container *root);
    
private:
    std::string m_name;
    
    Container *m_parent;
    
    int m_x, m_y;
    
    int m_width, m_height;
    
    bool m_visible;
    
protected:
    std::list< ActionListener* > m_listeners;

    SDL_Rect m_visiblePart;
    
    bool m_hasFocus;
    
public:

    /**
     * Constructor initializes the component's members using the parameter's
     * passed.
     *
     * @param name: component's name, an identifier for it in the system
     * @param w: component's width
     * @param h: component's height
     */

    Component(const std::string &name, int w, int h);


    /**
     * Constructor initializes the component's members using the parameter's
     * passed.
     *
     * @param parent: Component's 'parent node' in the composite
     * @param name: component's name, an identifier for it in the system
     * @param x: component's x coordinate inside its' container
     * @param y: component's y coordinate inside its' container
     * @param w: component's width
     * @param h: component's height
     */

    Component(const std::string &name, Container *parent, int x, int y, int w, int h);


    virtual ~Component();


    /**
     * Returns the name of the component.
     *
     * @return name of the component.
     */

    const std::string &GetName() const;


    /**
     * Returns the parent of this component.
     *
     * @return parent of the component.
     */

    Container *GetParent() const;


    /**
     * Returns the absolute screen position of a component.
     *
     * @param x: OUTPUT parameter, passed to save x on that var
     * @param y: OUTPUT parameter, passed to save y on that var
     */

    void GetPosition(int &x, int &y) const;
    
    
    /**
     * Returns relative coordinates from parent container.
     *
     * @param x: OUTPUT parameter, relative x is stored in here.
     * @param y: OUTPUT parameter, relative y is stored in here.
     */
    
    void GetRelativePosition(int &x, int &y) const;


    /**
     * Returns the absolute x screen coordinate of the component.
     *
     * @return absolute x of this comoponent.
     */

    int GetX() const;
    
    
    /**
     * Returns the relative x screen coordinate of the component.
     *
     * @return relative x of this component.
     */
    
    int GetRelativeX() const;


    /**
     * Retruns the absolute y screen coordinate of the component.
     *
     * @return absolute y of this component.
     */

    int GetY() const;
    
    
    /**
     * Returns the relative y screen coordinate of the component.
     *
     * @return relative y of this component.
     */
    
    int GetRelativeY() const;


    /**
     * Rerturns the width of the component.
     *
     * @return component's width.
     */

    int GetWidth() const;


    /**
     * Returns the height of the component.
     *
     * @return component's height.
     */

    int GetHeight() const;


    /**
     * Returns whether the Component is visible or not.
     *
     * @return a boolean value indicating visibility.
     */

    bool IsVisible() const;


    /**
     * Returns a rectangle containing the absolute boundaries of a Component.
     *
     * @return bounds of this component = { x, y, x + width, y + height };
     */

    SDL_Rect GetBounds() const;
    
    
    /**
     * Returns a rectangle containing the relative boundaries of a Component.
     *
     * @return relative bounds of this component = { rel_x, rel_y, rel_x + width, rel_y + height }
     */
    
    SDL_Rect GetRelativeBounds() const;
    
    
    /**
     * Returns the defininitive rectangle of this component.
     *
     * @return rectangle = { x, y, width, height }
     */
    
    SDL_Rect GetRect() const;
    
    
    /**
     * Returns the defininitive rectangle of this component, in relation to its parent.
     *
     * @return rectangle = { rel_x, rel_y, width, height }
     */
    
    SDL_Rect GetRelativeRect() const;


    /**
     * Sets the name of the component.
     *
     * @param name: to set
     */

    void SetName(const std::string &name);


    /**
     * Sets the parent of the component.
     *
     * @param parent: to set
     */

    void SetParent(Container *parent);


    /**
     * Sets the x coordinate of the component. The coordinate is translated in order
     * to relate the component's parent. E.g if the passed value is 10, and the
     * parent's x coordinate is 100 then the Component's absolute x on screen
     * is 100 + 10 = 110.
     *
     * @param x: to set
     */

    void SetX(int x);


    /**
     * Sets the y coordinate of the component. The coordinate is translated in order
     * to relate the component's parent. E.g if the passed value is 10, and the
     * parent's y coordinate is 100 then the Component's absolute y on screen
     * is 100 + 10 = 110.
     *
     * @param y: to set
     */

    void SetY(int y);


    /**
     * Sets the position of the component inside it's container. The x and y coord
     * values are converted to absolute screen coordinates.
     *
     * @param x: new x coordinate
     * @param y: new y coordinate
     */

    void SetPosition(int x, int y);
    
    
    /**
     * Resizes the component.
     *
     * @param width: new width.
     * @param height: new height.
     */
    
    virtual void Resize(int width, int height) = 0;
    

    /**
     * This routine makes the Component visible.
     */

    void Show();


    /**
     * This routine makes the Component invisible
     */


    void Hide();


    /**
     * This routine attaches an Action Listener to this Component
     */

    void AddActionListener(ActionListener *toAdd);


    /**
     * This routine detaches an Action Listener from this Component
     */

    void RemoveActionListener(ActionListener *toRemove);

    
    /**
     * This is invoked for a Component when a mouse event occurrs and the
     * Component has focus. Override to define specific component behaviour.
     *
     * @param event: the mouse event occurred.
     */
    
    virtual void OnMouseEvent(MouseEvent& event);
    
    
    /**
     * This is invoked for a Component when a keyboard event occurrs and the
     * Component has focus. Override to define specific component behaviour.
     *
     * @param event: the keyboard event occurred.
     */ 
    
    virtual void OnKeyboardEvent(KeyboardEvent& event);
    

    /**
     * Draws the component on a surface.
     * Abstract function, must be implemented in each concrete component.
     *
     * @param target: surface to draw component on.
     */

    virtual void Draw(sdl::Surface *target) const = 0;

    
    /**
     * Gives focus to this component.
     */
    
    void Focus();
    

    /**
     * Unfocuses this component.
     */
    
    void Unfocus();
    
    
    /**
     * @return a boolean value indicating if the component has focus or not.
     */
    
    bool HasFocus() const;
    
    
    /**
     * Routine called when needed to recompute the visible part of this Component.
     */
    
    void UpdateVisiblePart();
    
    
    /**
     * This is used to offer run time information about a component. If this is a regular Component
     * NULL is returned. Container should override this one to return a pointer to itself.
     */
    
    virtual Container *GetContainer();

protected:
    
    /**
     * @param event: a MouseEvent occurred.
     * @param mouseX: mouse X coordinate.
     * @param mouseY: mouse Y coordinate.
     */

    bool NeedsFocus(int mouseX, int mouseY) const;
    

    /**
     * Sets the width of the component.
     *
     * @param width: to set
     */

    void SetWidth(int width);


    /**
     * Sets the height of the component.
     *
     * @param height: to set
     */

    void SetHeight(int height);
    
    
    /**
     * Returns the visible part of this Component. The coordinates
     * of the rectangle returned are the absolute screen ones.
     *
     * @return the rectangle containing the absolute coordinates of the visible part.
     */
    
    SDL_Rect GetVisibleRect() const;
    
    
    /**
     * Returns the visible part of this Component. The coordinates
     * of the rectangle returned are relative to the Componenent's parent
     *
     * @return the rectangle containing the relative coordinates of the visible part.
     */
    
    SDL_Rect GetRelVisibleRect() const;
};

};

#endif	/* COMPONENT_H */

